package dipl.visualizer.graphics.shape;

import dipl.algorithm.math.curve.BezierCurve;
import dipl.algorithm.math.utility.ApfloatUtils;
import dipl.algorithm.math.utility.FPMath;
import dipl.algorithm.math.primitive.Point2d;
import dipl.algorithm.math.fp.primitive.Point2df;
import dipl.algorithm.exception.ArgumentException;
import dipl.visualizer.graphics.PaintContext;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Stroke;

/**
 *
 */
public class BezierCurveShape extends AbstractShape {

	//
	// CONSTRUCTOR METHODS
	//
	
	/// <summary>
	/// Constructs shape from curve.
	/// </summary>
	/// <exception cref="Exception">Thrown if numPointsToCalculate lesser or equal 0</exception>
	/// <param name="name">name of shape</param>
	/// <param name="curve"></param>
	/// <param name="numPointsToCalculate">number of curve points to calculate</param>
	/// <param name="color">paint color</param>
	/// <param name="font">text font</param>
	/// <param name="paintControlPolygon">whether or not to paint control polygon</param>
	public BezierCurveShape(
		FPMath fpMath,
		String name,
		BezierCurve curve,
		int numPointsToCalculate,
		Color color,
		Font font,
		boolean paintControlPolygon
	) throws ArgumentException {

		super( name, color );
		if( numPointsToCalculate <= 0 )
			throw new ArgumentException();

		this.curve = curve;
		this.numPointsToCalculate = numPointsToCalculate;
		Point2d[] temp =  new Point2d[numPointsToCalculate];
		for( int i = 0; i < numPointsToCalculate; i++ ) {
			temp[i] = new Point2d();
		}
		this.curve.CalculatePoints( fpMath, temp );

		this.curvePoints = new Point2df[numPointsToCalculate];
		for( int i = 0; i < numPointsToCalculate; i++ ) {
			this.curvePoints[i] = new Point2df( temp[i].x.doubleValue(), temp[i].y.doubleValue() );
		}

		this.curveControls = new Point2df[curve.Degree()+1];
		for( int i = 0; i <curveControls.length; i++ ) {
			this.curveControls[i] = new Point2df( curve.Controls()[i].x.doubleValue(), curve.Controls()[i].y.doubleValue() );
		}

		Point2d poc = curve.Calculate( fpMath, curve.Domain().L().add( curve.Domain().U() ).multiply( ApfloatUtils.ValueOf( 0.5 ) ) );
		pointOnCurve = new Point2df( poc.x.doubleValue(), poc.y.doubleValue() );
		
		this.font = font;
		this.paintControlPolygon = paintControlPolygon;
		this.curveStroke = new BasicStroke( 2.0f );
		float dash1[] = { 10.0f };
		this.polyStroke =
			new BasicStroke( 2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f );
	}

	//
	// PUBLIC METHODS
	//
	
	@Override
	public void Paint( PaintContext pc ) {
		int i, n; // indices
		
		// paint curve
		pc.SetColor( color );
		pc.SetStroke( curveStroke );
		for( i = 0; i < numPointsToCalculate-1; i++ ) {
			pc.DrawLine( curvePoints[i], curvePoints[i+1] );
		}

		if( !paintControlPolygon )
			return;

		n = curveControls.length;

		// paint control polygon
		pc.SetStroke( polyStroke );
		for( i = 0; i < n; i++ ) {
			pc.DrawLine( curveControls[i], curveControls[(i+1)%n] );
		}
		
		// paint control points
		pc.SetColor( Color.black );
		pc.SetFont( font );
		for( i = 0; i < n; i++ ) {
			pc.FillRectangle( curveControls[i].x-0.01, curveControls[i].y+0.01, 0.02, 0.02 );
			pc.DrawString( Integer.toString( i ), curveControls[i].x+0.02, curveControls[i].y+0.02 );
		}
	}

	//
	// PROPERTIES
	//
	
	/// <summary>
	/// Returns curve
	/// </summary>
	public BezierCurve Curve() {
		return curve;
	}

	@Override
	public Point2df PointOnShape() {
		return pointOnCurve;
	}

	public void SetCurveStroke( Stroke stroke ) {
		this.curveStroke = stroke;
	}

	//
	// MEMBERS
	//
					
	/// <summary>
	/// curve
	/// </summary>
	private BezierCurve curve;
	/// <summary>
	/// whether or not to paint control polygon
	/// </summary>
	private boolean paintControlPolygon;
	/// <summary>
	/// Font for painting text
	/// </summary>
	private Font font;

	private Stroke curveStroke;

	private Stroke polyStroke;
	
	/// <summary>
	/// calculated points on curve
	/// </summary>
	private Point2df[] curvePoints;
	/// <summary>
	/// calculated points on curve
	/// </summary>
	private Point2df[] curveControls;
	///
	/// a point on the curve
	///
	private Point2df pointOnCurve;

	/// <summary>
	/// number of points to calculate
	/// </summary>
	private int numPointsToCalculate;
}
